package org.promote.hotspot.client.netty;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.promote.hotspot.client.ClientContext;
import org.promote.hotspot.client.callback.ReceiveNewKeyEvent;
import org.promote.hotspot.client.eventbus.EventBusCenter;
import org.promote.hotspot.common.model.HotKeyModel;
import org.promote.hotspot.common.model.HotSpotMsg;
import org.promote.hotspot.common.model.MessageType;

/**
 * @author enping.jep
 * @date 2023/11/17 14:58
 **/
@Slf4j
public class NettyClientHandler extends SimpleChannelInboundHandler<HotSpotMsg> {
    /**
     * userEventTriggered处理心跳超时事件，在IdleStateHandler设置超时时间，
     * 如果达到了，就会直接调用该方法。如果没有超时则不调用
     *
     * @param ctx 用于处理事件和操作的上下文信息
     * @param evt 事件类型
     * @throws Exception
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
            if (idleStateEvent.state() == IdleState.ALL_IDLE) {
                HotSpotMsg hotSpotMsg = new HotSpotMsg(MessageType.PING, ClientContext.APP_NAME);
                //向服务端发送消息
//                log.info("向服务端发送心跳检测信息 == " + hotSpotMsg.toString());
                ctx.writeAndFlush(hotSpotMsg);
            }
        }
        super.userEventTriggered(ctx, evt);
    }

    /**
     * channelActive: 当 Channel 激活（即已连接到远程主机）时触发
     *
     * @param ctx 用于处理事件和操作的上下文信息
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        log.info("channelActive:" + ctx.name());
        ctx.writeAndFlush(new HotSpotMsg(MessageType.APP_NAME, ClientContext.APP_NAME));
    }

    /**
     * 当从服务器接收到一条消息时被调用
     *
     * @param ctx the {@link ChannelHandlerContext} which this {@link SimpleChannelInboundHandler}
     *            belongs to
     * @param msg the message to handle
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HotSpotMsg msg) throws Exception {
        if (MessageType.PONG == msg.getMessageType()) {
//            log.info("收到服务端的心跳检测回执 == " + msg.toString());
            return;
        }

        if (MessageType.RESPONSE_NEW_KEY == msg.getMessageType()) {
            log.info("receive new key : " + msg);
            if (CollectionUtils.isEmpty(msg.getHotKeyModels())) {
                return;
            }
            for (HotKeyModel model : msg.getHotKeyModels()) {
                EventBusCenter.getInstance().post(new ReceiveNewKeyEvent(model));
            }
        }
    }

    /**
     * 断线处理
     * channelInactive()方法是在Socket连接关闭时，通过挥手数据包触发的，可以用来感知正常的下线情况，但对于网络异常等非正常下线情况则无法感知
     * 1、客户端发送了一个关闭连接的信号，例如发送了一个FIN包。
     * 应用程序退出：如果客户端应用程序正在运行并且由于用户退出、崩溃或其他原因而终止，操作系统可能会发送一个FIN包来关闭任何打开的TCP连接。
     * 2、客户端关闭了进程，导致发送了一个RST包。
     * 当发送RST包时，发送方表明它希望立即结束连接，并且不关心连接的另一端是否已经准备好关闭。接收到RST包的一方会立即关闭连接，不再发送任何数据。
     * 3、服务端或客户端通过调用channel.close()方法主动关闭了连接。
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
        EventBusCenter.getInstance().post(new ChannelInactiveEvent(ctx.channel()));
    }


}